﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text.RegularExpressions;
using CrawlerCore;
using HtmlAgilityPack;

namespace WCrawlerLib
{
    public abstract class BrowserSession_vBulletin : BrowserSession_Forum
    {
        public BrowserSession_vBulletin()
        {
            ForumTypeId = enumForumType.vBulletin;
        }

        #region Properties
        

        public string SessionHash
        {
            get
            {
                //try
                //{
                foreach (Cookie objCookie in Cookies)
                {
                    if (objCookie.Name.Contains("sessionhash"))
                        return objCookie.Value;
                }
                //========================================================
                //if (_cookies["bb_sessionhash"] != null)
                //    return _cookies["bb_sessionhash"].Value;
                //else if (_cookies["bbsessionhash"] != null)
                //{
                //    return _cookies["bbsessionhash"].Value;
                //}
                //========================================================
                //}
                //catch
                //{
                //    try
                //    {
                //        if (_cookies["bbsessionhash"] != null)
                //            return _cookies["bbsessionhash"].Value;
                //    }
                //    catch
                //    {

                //    }
                //}
                return "";
            }
        }
        #endregion

        /// <summary>
        /// Get user id
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="userName"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        public override int GetUserId(string forumUrl, string userName)
        {
            int userId = -1;
            //string xPath = ".//a[(contains(@href, '[username]') or contains(.//text(), '[username]')) and contains(@href, 'member')]".Replace("[username]", userName);
            string xPath = ".//a[contains(@href, '[username]') or contains(.//text(), '[username]')]".Replace("[username]", userName);
            string profileUrl = HtmlDoc.DocumentNode.SelectNodes(xPath)[0].Attributes["href"].Value;
            profileUrl = GeneralClass.GetFullUrl(forumUrl, profileUrl);
            Regex r = new Regex(@"member\.php\?u=(?<userid>([0-9]+))");
            Match m = r.Match(profileUrl);
            if (m.Success)
                userId = int.Parse(m.Result("${userid}"));
            else
            {
                Get(profileUrl);
                m = r.Match(HtmlDoc.DocumentNode.InnerHtml);
                if (m.Success)
                    userId = int.Parse(m.Result("${userid}"));
                //string result = loggedInUser;
                //if (!string.IsNullOrEmpty(result))
                //    userId = int.Parse(result);
            }
            return userId;
        }

        /// <summary>
        /// Login in a forum. If susscessful set _isLogged = true, else false
        /// </summary>
        public override bool DoLogin(string forumUrl, string loginUrl, string postParams, string username, string password)
        {
            bool loginStatus= false;
            try
            {
                //Init login params
                _postParams = InitPostParams(enumPost.Login, postParams, username, password);
                loginUrl = GeneralClass.GetFullUrl(forumUrl, loginUrl);
                Post(loginUrl);

                //if login fail
                if (Response.StatusCode != HttpStatusCode.OK)
                    return loginStatus;
                
                //If count > 1 successfully log in
                if (!string.IsNullOrEmpty(SessionHash))
                {
                    loginStatus = true;
                }

                //else
                //{
                //    //string content = HtmlDoc.DocumentNode.InnerHtml.ToLower();
                //    //List<string> key = new List<string>();
                //    //string pattern = @"(sign in|log in|register\s*(now|)|forgot(ten|)\s+(your|my|)\s+password|(please|)\s+try again|(incorrect|invalid) username|remember me)";
                //    //Regex r = new Regex(pattern);
                //    //MatchCollection m = r.Matches(content);

                //    ////If count > 1 log in invalid username or password
                //    //if (m.Count > 1)
                //    //    loginStatusId = 2;
                //    //else //Error log in 
                //    //    loginStatusId = 3;
                //}
            }
            catch (WebException we)
            {
                throw we;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return loginStatus;
        }


        public override string FValue_Pattern()
        {
            return @"do\=newthread\&f=(?<fValue>([0-9]+))";
        }
        

        /// <summary>
        /// Get group title include titles of subforums 
        /// vBulletin3x: .//table[@id='threadslist']/tbody/tr[./td[contains(@id, 'td_threadtitle')]]
        /// vBulletin4x: .//li[contains(@id,'thread')]/div
        /// </summary>
        /// <param name="forum"></param>
        /// <param name="xPath"></param>
        /// <returns></returns>
        public override DataTable SearchForumThreads(string forumUrl, string statisticUrl, string xPath_Paging, DataTable table, bool isPaging, bool isGetFirstThreads)
        {
            xPath_Paging = @".//a[contains(@href, '&page=')]";
            table = base.SearchForumThreads(forumUrl, statisticUrl, xPath_Paging, table, isPaging, isGetFirstThreads);
            return table;

            //List<string> urlList = new List<string>();
            //statisticUrl = GeneralClass.GetFullUrl(forumUrl, statisticUrl);
            //Get(statisticUrl);
            ////Search threads in first statistic page
            //List<DataRow> rowList = SearchThreadsByPaging(forumUrl, HtmlDoc, table);
            //string[] result = new string[2];
            //foreach (DataRow row in rowList)
            //{
            //    result = GetThreadPostNum(row["Url"].ToString());
            //    row["ThreadNum"] = result[0];
            //    row["PostNum"] = result[1];
            //    table.Rows.Add(row);
            //    //if only get first thread
            //    if (isGetFirstThreads) 
            //        return table;
            //}

            ////Search threads in next statistic pages in paging
            //if (isPaging)
            //{
            //    //Find url of pages if paging
            //    string xPath = @".//a[contains(@href, '&page=')]";
            //    HtmlNodeCollection nodeCollection = HtmlDoc.DocumentNode.SelectNodes(xPath);
            //    if (nodeCollection == null)
            //        return table;

            //    object lockRow = new object();
            //    object lockList = new object();
            //    Parallel.ForEach<HtmlNode>(nodeCollection, node =>
            //    {
            //        //get paging urls
            //        statisticUrl = node.Attributes["href"].Value;
            //        if (!urlList.Contains(statisticUrl))
            //        {
            //            lock (lockList)
            //            {
            //                urlList.Add(statisticUrl);
            //            }
            //            statisticUrl = GeneralClass.GetFullUrl(forumUrl, statisticUrl);
            //            //Load statistic url of pagings
            //            HtmlDocument doc = Get(statisticUrl);
            //            List<DataRow> rowList1 = SearchThreadsByPaging(forumUrl, doc, table);

            //            //foreach (DataRow row in rowList1)
            //            Parallel.ForEach<DataRow>(rowList1, row =>
            //            {
            //                result = GetThreadPostNum(row["Url"].ToString());
            //                row["ThreadNum"] = result[0];
            //                row["PostNum"] = result[1];
            //                lock (lockRow)
            //                {
            //                    table.Rows.Add(row);
            //                }
            //            });
            //        }
            //    });
            //}
            //return table;
        }

        public string InitPostParams(enumPost postType, string postParams, params string[] pars)
            //string username, string password, //use for login
            //string subject, string message, string fId, string iconId, string tagList, string prefixId, //use for newthread post
            //string postid, string securToken_editpost, string updateReason) //use for edit post
        {
            Dictionary<string, string> paramDict = new Dictionary<string, string>();
            if (postType == enumPost.Login)
            {
                //=============================================================================
                //LOGIN PARAMS
                paramDict.Add("[vb_login_username]", pars[0]);
                paramDict.Add("[vb_login_password_hint]", HtmlDoc.GetValueInInputTag("text", "name", "vb_login_password_hint"));
                paramDict.Add("[vb_login_password]", "");
                paramDict.Add("[s]", "");
                paramDict.Add("[cookieuser]", "checked");
                paramDict.Add("[securitytoken]", "guest");
                paramDict.Add("[do]", "login");
                paramDict.Add("[vb_login_md5password]", GetMD5Hash(pars[1]));
                paramDict.Add("[vb_login_md5password_utf]", GetMD5Hash(pars[1]));
            }
            else if (postType == enumPost.NewThread)
            {
                //"newthread", postParams, subject, message, fId, icon, tagList, prefixId;
                //=============================================================================
                //NEWTHREAD POST PARAMS
                paramDict.Add("[prefixid]", pars[5]);
                paramDict.Add("[subject]", pars[0]);
                paramDict.Add("[message]", pars[1].Replace("&", "%26"));
                //paramDict.Add("[wysiwyg]", "0");//wysiwyg);
                paramDict.Add("[taglist]", pars[4]);
                paramDict.Add("[sendtrackbacks]", "");
                paramDict.Add("[iconid]", string.IsNullOrEmpty(pars[3]) ? "0" : pars[3]);
                paramDict.Add("[s]", SessionHash);
                paramDict.Add("[securitytoken]", HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"));
                paramDict.Add("[f]", pars[2]);
                paramDict.Add("[do]", "postthread");
                //paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)"));
                //paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)"));
                //paramDict.Add("[loggedinuser]", HtmlDoc.GetValueInInputTag("hidden", "name", "loggedinuser"));
                //paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
                //paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
                paramDict.Add("[sessionhash]", SessionHash);
                //others
                paramDict.Add("[vbseo_retrtitle]", "1");
                paramDict.Add("[vbseo_is_retrtitle]", "1");
                //paramDict.Add("[emailupdate]", "9999");
                paramDict.Add("[polloptions]", "4");
            }
            else if (postType == enumPost.EditPost)
            {
                //subject, message + "\n" + updateReason, postId, HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"), updateReason, iconId);
                //=============================================================================
                //EDIT POST PARAMS
                paramDict.Add("[reason]", pars[4]);
                paramDict.Add("[title]", pars[0]);
                paramDict.Add("[message]", pars[1].Replace("&", "%26"));
                //paramDict.Add("[wysiwyg]", "0");
                paramDict.Add("[iconid]", string.IsNullOrEmpty(pars[5]) ? "0" : pars[5]);
                paramDict.Add("[signature]", "1");
                //paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
                paramDict.Add("[vbseo_retrtitle]", "1");
                paramDict.Add("[vbseo_is_retrtitle]", "1");
                //paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
                paramDict.Add("[s]", SessionHash);
                paramDict.Add("[securitytoken]", pars[3]);
                paramDict.Add("[do]", "updatepost");
                paramDict.Add("[p]", pars[2]);
                //paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)")); //4x
                //paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)")); //4x
                //paramDict.Add("[emailupdate]", "9999"); //4x
            }
            else if (postType == enumPost.ReplyPost)
            {
                //=============================================================================
                //REPLY POST PARAMS
                paramDict.Add("[securitytoken]", HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"));
                paramDict.Add("[ajax_lastpost]", HtmlDoc.GetValueByRegexPattern(@"var\sajax_last_post\s\=\s(?<value>[0-9]+)"));
                paramDict.Add("[message]", pars[0].Replace("&", "%26"));
                //paramDict.Add("[wysiwyg]", "0");//wysiwyg);
                paramDict.Add("[fromquickreply]", HtmlDoc.GetValueInInputTag("hidden", "name", "fromquickreply"));
                //paramDict.Add("[s]", SessionHash);
                paramDict.Add("[threadid]", HtmlDoc.GetValueInInputTag("hidden", "name", "t"));
                paramDict.Add("[topicid]", HtmlDoc.GetValueInInputTag("hidden", "name", "p"));
                paramDict.Add("[specifiedpost]", HtmlDoc.GetValueInInputTag("hidden", "name", "specifiedpost"));
                //paramDict.Add("[loggedinuser]", HtmlDoc.GetValueInInputTag("hidden", "name", "loggedinuser"));
                //paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)"));
                //paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)"));
                //paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
                //paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
                paramDict.Add("[styleid]", HtmlDoc.GetValueInInputTag("hidden", "name", "styleid")); //3x
            }

            //=============================================================================
            //SAME PARAMS
            paramDict.Add("[wysiwyg]", "0");
            paramDict.Add("[loggedinuser]", HtmlDoc.GetValueInInputTag("hidden", "name", "loggedinuser"));
            paramDict.Add("[posthash]", HtmlDoc.GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)"));//4x
            if (string.IsNullOrEmpty(paramDict["[posthash]"]))
                paramDict["[posthash]"] = HtmlDoc.GetValueInInputTag("hidden", "name", "posthash"); //3x

            paramDict.Add("[poststarttime]", HtmlDoc.GetValueByRegexPattern("poststarttime\\s\\(:|=)\\s\"(?<value>[0-9]+)")); //4x
            if (string.IsNullOrEmpty(paramDict["[poststarttime]"]))
                paramDict["[poststarttime]"] = HtmlDoc.GetValueInInputTag("hidden", "name", "poststarttime"); //3x

            paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
            if (string.IsNullOrEmpty(paramDict["[sbutton]"]))
                paramDict["[sbutton]"] = HtmlDoc.GetValueInInputTag("submit", "name", "sbutton"); //3x

            paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
            if (string.IsNullOrEmpty(paramDict["[parseurl]"]))
                paramDict["[parseurl]"] = HtmlDoc.GetValueInInputTag("checkbox", "name", "parseurl"); //3x
            
            paramDict.Add("[emailupdate]", "9999");

            foreach (KeyValuePair<string, string> par in paramDict)
            {
                if (postParams.Contains(par.Key))
                    postParams = postParams.Replace(par.Key, par.Value);
            }
            return postParams;
        }

        ///// <summary>
        ///// Init params of POST  to login a forum.
        ///// </summary>
        //public override string InitLoginParams(string postParams, params string[] pars)//(string username, string password)
        //{
        //    ////Method 1:
        //    //string password = GetMD5Hash(pars[1]);
        //    //_postParams = "vb_login_username=" + pars[0]
        //    //        + "&vb_login_password="
        //    //        + "&s="
        //    //        + "&cookieuser=" + "checked"
        //    //        + "&securitytoken=" + "guest"
        //    //        + "&do=" + "login"
        //    //        + "&vb_login_md5password=" + password
        //    //        + "&vb_login_md5password_utf=" + password;

        //    ////set post paramaters
        //    //return _postParams;

        //    string password = GetMD5Hash(pars[1]);
        //    //========================================================================
        //    //Init new thread params
        //    Dictionary<string, string> paramDict = new Dictionary<string, string>();
        //    paramDict.Add("[vb_login_username]", pars[0]);
        //    paramDict.Add("[vb_login_password_hint]", HtmlDoc.GetValueInInputTag("text", "name", "vb_login_password_hint"));
        //    paramDict.Add("[vb_login_password]", "");
        //    paramDict.Add("[s]", "");
        //    paramDict.Add("[cookieuser]", "checked");
        //    paramDict.Add("[securitytoken]", "guest");
        //    paramDict.Add("[do]", "login");
        //    paramDict.Add("[vb_login_md5password]", password);
        //    paramDict.Add("[vb_login_md5password_utf]", password);

        //    foreach (KeyValuePair<string, string> par in paramDict)
        //    {
        //        if (postParams.Contains(par.Key))
        //            postParams = postParams.Replace(par.Key, par.Value);
        //    }
        //    return postParams;
        //}

        ///// <summary>
        ///// Init params of new thread
        ///// </summary>
        ///// <param name="postParams"></param>
        ///// <param name="subject"></param>
        ///// <param name="message"></param>
        ///// <param name="fId"></param>
        ///// <param name="tagList"></param>
        ///// <returns></returns>
        //public override string InitNewThreadParams(string postParams, params string[] values)// string subject, string message, string fId, string icon, string tagList)
        //{
        //    //========================================================================
        //    //Init new thread params
        //    Dictionary<string, string> paramDict = new Dictionary<string, string>();
        //    paramDict.Add("[prefixid]", values[5]);
        //    paramDict.Add("[subject]", values[0]);
        //    paramDict.Add("[message]", values[1].Replace("&", "%26"));
        //    paramDict.Add("[wysiwyg]", "0");//wysiwyg);
            
        //    paramDict.Add("[taglist]", values[4]);
        //    paramDict.Add("[sendtrackbacks]", string.Empty);
        //    paramDict.Add("[iconid]", values[3]);
        //    paramDict.Add("[s]", SessionHash);
        //    paramDict.Add("[securitytoken]", HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"));
        //    paramDict.Add("[f]", values[2]);
        //    paramDict.Add("[do]", "postthread");
        //    paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)"));
        //    paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)"));
        //    paramDict.Add("[loggedinuser]", HtmlDoc.GetValueInInputTag("hidden", "name", "loggedinuser"));
        //    paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
        //    paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
        //    paramDict.Add("[sessionhash]", SessionHash);
        //    //others
        //    paramDict.Add("[vbseo_retrtitle]", "1");
        //    paramDict.Add("[vbseo_is_retrtitle]", "1");
        //    paramDict.Add("[emailupdate]", "9999");
        //    paramDict.Add("[polloptions]", "4");
        //    foreach (KeyValuePair<string, string> par in paramDict)
        //    {
        //        if (postParams.Contains(par.Key))
        //            postParams = postParams.Replace(par.Key, par.Value);
        //    }
        //    return postParams;
        //}

        ///// <summary>
        ///// Init params of new reply
        ///// </summary>
        ///// <param name="postParams"></param>
        ///// <param name="message"></param>
        ///// <returns></returns>
        //public override string InitNewReplyParams(string postParams, string message)
        //{
        //    //========================================================================
        //    //Init new thread params
        //    Dictionary<string, string> paramDict = new Dictionary<string, string>();
        //    paramDict.Add("[securitytoken]", HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"));
        //    paramDict.Add("[ajax_lastpost]", GetValueByRegexPattern(@"var\sajax_last_post\s\=\s(?<value>[0-9]+)"));
        //    paramDict.Add("[message]", message.Replace("&", "%26"));
        //    paramDict.Add("[wysiwyg]", "0");//wysiwyg);
        //    paramDict.Add("[fromquickreply]", HtmlDoc.GetValueInInputTag("hidden", "name", "fromquickreply"));
        //    paramDict.Add("[s]", SessionHash);
        //    paramDict.Add("[threadid]", HtmlDoc.GetValueInInputTag("hidden", "name", "t"));
        //    paramDict.Add("[topicid]", HtmlDoc.GetValueInInputTag("hidden", "name", "p"));
        //    paramDict.Add("[specifiedpost]", HtmlDoc.GetValueInInputTag("hidden", "name", "specifiedpost"));
        //    paramDict.Add("[loggedinuser]", HtmlDoc.GetValueInInputTag("hidden", "name", "loggedinuser"));
        //    paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)"));
        //    paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)"));
        //    paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
        //    paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
        //    paramDict.Add("[styleid]", HtmlDoc.GetValueInInputTag("hidden", "name", "styleid")); //3x

        //    foreach (KeyValuePair<string, string> par in paramDict)
        //    {
        //        if (postParams.Contains(par.Key))
        //            postParams = postParams.Replace(par.Key, par.Value);
        //    }
        //    return postParams;
        //}

        ///// <summary>
        ///// Init params of a post update
        ///// Notes: "securityToken" and "relpath" not get direct in current _htmDoc
        ///// </summary>
        ///// <param name="postParams"></param>
        ///// <param name="message"></param>
        ///// <returns></returns>
        //public string InitEditPostParams(string postParams, params string[] values)//string postid, string securToken, string title, string message, string updateReason, string iconId)
        //{
        //    //========================================================================
        //    //Init new thread params
        //    Dictionary<string, string> paramDict = new Dictionary<string, string>();
        //    paramDict.Add("[reason]", values[4]);
        //    paramDict.Add("[title]", values[2]);
        //    paramDict.Add("[message]", values[3].Replace("&", "%26"));
        //    paramDict.Add("[wysiwyg]", "0");
        //    paramDict.Add("[iconid]", string.IsNullOrEmpty(values[5]) ? "0" : values[5]);
        //    paramDict.Add("[signature]", "1");
        //    paramDict.Add("[parseurl]", HtmlDoc.GetValueInInputTag("hidden", "name", "parseurl"));
        //    paramDict.Add("[vbseo_retrtitle]", "1");
        //    paramDict.Add("[vbseo_is_retrtitle]", "1");
        //    paramDict.Add("[sbutton]", HtmlDoc.GetValueInInputTag("hidden", "id", "qr_submit"));
        //    paramDict.Add("[s]", SessionHash);
        //    paramDict.Add("[securitytoken]", values[1]);
        //    paramDict.Add("[do]", "updatepost");
        //    paramDict.Add("[p]", values[0]);
        //    paramDict.Add("[posthash]", GetValueByRegexPattern("posthash\\s\\:\\s\"(?<value>[0-9]+)")); //4x
        //    paramDict.Add("[poststarttime]", GetValueByRegexPattern("poststarttime\\s\\:\\s\"(?<value>[0-9]+)")); //4x
        //    paramDict.Add("[emailupdate]", "9999"); //4x
            
        //    foreach (KeyValuePair<string, string> par in paramDict)
        //    {
        //        if (postParams.Contains(par.Key))
        //            postParams = postParams.Replace(par.Key, par.Value);
        //    }
        //    return postParams;
        //}

        /// <summary>
        /// Post a new thread
        /// .//div[contains(@id,'post_message')]
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="newThreadUrl"></param>
        /// <param name="postParams"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="fId"></param>
        /// <param name="icon"></param>
        /// <param name="tagList"></param>
        /// <param name="prefixId"></param>
        public override void PostNewThread(string forumUrl, string newThreadUrl, string postParams, string subject, string message, string fId, string icon, string tagList, string prefixId)
        {
            //string id = ""; 
            //Step1: Get full url new thread and load url
            newThreadUrl = newThreadUrl.Replace("[fid]", fId.ToString()).Replace("[sessionhash]", SessionHash);
            newThreadUrl = GeneralClass.GetFullUrl(forumUrl, newThreadUrl);
            HtmlDocument doc = Get(newThreadUrl);
            
            //Step2: Find post url in new thread page
            string postNewThreadUrl = "";
            HtmlNodeCollection nodeCollection = doc.DocumentNode.SelectNodes(".//form[@method='post' and contains(@action, 'newthread.php?do=postthread')]");
            if (nodeCollection == null)
                return;
            else
            {
                foreach (HtmlNode node in nodeCollection)
                {
                    if (!string.IsNullOrEmpty(node.OuterHtml))
                    {
                        //string newThreadUrl = node.Attributes["action"].Value;
                        postNewThreadUrl = GeneralClass.GetFullUrl(forumUrl, node.Attributes["action"].Value);
                    }
                }
            }

            //Step3: Get POST Params 
            //_postParams = InitNewThreadParams(postParams, subject, message, fId, icon, tagList, prefixId);
            _postParams = InitPostParams(enumPost.NewThread, postParams, subject, message, fId, icon, tagList, prefixId);
            
            //Step4: Finally start post new thead and return thread num
            Post(postNewThreadUrl);
        }

        
        /// <summary>
        /// Update content of a thread have been posted
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="topicUrl"></param>
        /// <param name="postUpdateUrl"></param>
        /// <param name="postParams"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="fId"></param>
        /// <param name="tagList"></param>
        /// <returns></returns>
        public override bool EditPost(string forumUrl, string topicUrl, string postId, string editParams, string subject, string message, string updateReason, string iconId)
        {
            bool result = false;
            //Step1: Load topic url
            Get(forumUrl + "/" + topicUrl);

            //Step2: Load edit page
            //xpath = .//a[starts-with(@name,'vB::QuickEdit::')] is vBulletin4x
            //xpath = .//a[@class='editpost'] is vBulletin3x
            string xPath = ".//a[starts-with(@name,'vB::QuickEdit::') or @class='editpost']";
            HtmlNodeCollection nodeCollection = HtmlDoc.DocumentNode.SelectNodes(xPath);
            if (nodeCollection == null)
                return result;

            string editUrl = nodeCollection[0].Attributes["href"].Value;
            editUrl = GeneralClass.GetFullUrl(forumUrl, editUrl);
            Get(editUrl);

            //Step3: Init post params
            //_postParams = InitEditPostParams(editParams, postId, HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"), subject, message + "\n" + updateReason, updateReason, iconId);
            _postParams = InitPostParams(enumPost.EditPost, editParams, subject, message + "\n" + updateReason, postId, HtmlDoc.GetValueInInputTag("hidden", "name", "securitytoken"), updateReason, iconId);

            //Step4: Start post update
            xPath = ".//form[contains(@action,'editpost') and @method='post']";
            nodeCollection = HtmlDoc.DocumentNode.SelectNodes(xPath);
            if (nodeCollection == null)
                return result;
            string postUrl = nodeCollection[0].Attributes["action"].Value;
            postUrl = GeneralClass.GetFullUrl(forumUrl, postUrl);
            Post(postUrl);

            if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                result = true;
            return result;
        }

        /// <summary>
        /// Post a new thread
        /// .//div[contains(@id,'post_message')]
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="fId"></param>
        /// <returns></returns>
        public override bool PostNewReply(string forumUrl, string topicUrl, string newReplyUrl, string postParams, string message)
        {
            bool result = false;
            //Load topic url
            Get(forumUrl + "/" + topicUrl);
            //Init post reply url
            string postReplyUrl = forumUrl + "/" + newReplyUrl.Replace("[threadid]", HtmlDoc.GetValueInInputTag("hidden", "name", "t"));
            //Init post params
            //_postParams = InitNewReplyParams(postParams, message);
            _postParams = InitPostParams(enumPost.ReplyPost, postParams, message);
            //Start post reply
            Post(postReplyUrl);

            if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                result = true;
            return result;
        }

        /// <summary>
        /// 4x: .//ol[@id='threads']/li[.//a[.//a[contains(@id,'thread_title') and contains(text(),'[postTitle]')]]
        /// 3x: .//table[@id='threadslist']//td[contains(@id,'td_threadtitle') and .//a[contains(text(),'[postTitle]')]]
        /// </summary>
        /// <param name="forumUrl"></param>
        /// <param name="topicUrl"></param>
        /// <returns></returns>
        public override string SearchThreadsInTopic(string postTitle, string topicUrl, string searchTopic_xPath, string username)
        {
            string threadUrl = "";
            HtmlDocument doc = Get(topicUrl);
            string xpath = string.Format(searchTopic_xPath, postTitle);

            HtmlNodeCollection nodeCollect = doc.DocumentNode.SelectNodes(xpath);
            if (nodeCollect != null)
            {
                foreach (HtmlNode node in nodeCollect)
                {
                    if (node.InnerHtml.Contains(username))
                    {
                        HtmlNode node1 = node.SelectSingleNode(".//a[contains(@id,'thread_title')]");
                        threadUrl = node1.Attributes["href"].Value;
                        break;
                    }
                }
            }
            return threadUrl;
        }
    }
}